<?php
class YMSG
{
	// 0x57 : W, 0x54 : T
	var	$serviceCode 	= array('getChallenge' => 0x57, 'loginRequest' => 0x54);

	var $clientVer		= '9.0.0.2034';	//'9.0.0.1389';
	var $protocolVer	= '16';
	var	$sepStr;

	// Define Separate
	public function __construct()
	{
		$this->sepStr = chr(0xC0).chr(0x80);
	}
	public function curl($url,$method = 'GET',$vars="",$fl = 0,$head = 0,$nobody = 0)
	{
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL,$url);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
		if ($method == 'POST') {
		curl_setopt($ch, CURLOPT_POST, 1);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $vars);
		}
		curl_setopt($ch, CURLOPT_USERAGENT,$_SERVER['HTTP_USER_AGENT']);
		@curl_setopt($ch, CURLOPT_FOLLOWLOCATION,$fl);
		curl_setopt($ch, CURLOPT_HEADER,$head);
		curl_setopt($ch, CURLOPT_NOBODY,$nobody);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
		curl_setopt($ch, CURLOPT_COOKIEFILE, "filecookie");
		curl_setopt($ch, CURLOPT_COOKIEJAR, "filecookie");
		ob_start();
		$result = curl_exec($ch);
		curl_close($ch);
		ob_end_clean();
		return $result;
	}
	public function getServerIP()
	{
		//$server=$this->get_Cache("SERVERYahoo");
		//if(!empty($server))return $server;
		// Connect to server
		$server	= file_get_contents('http://vcs1.msg.yahoo.com/capacity');
		$server	= substr($server, strpos($server, "CS_IP_ADDRESS=") + strlen("CS_IP_ADDRESS="));
		$server	= trim($server);
		//$this->set_Cache("SERVERYahoo",$server);
		return $server;
	}
	// Return YMSG header
	public function getHeader($length)
	{
		$value	= "YMSG".chr(0x0).chr($this->protocolVer).chr(0x0).chr(0x0);
		$value	.=	chr(intval($length/256));
		$value	.=	chr(fmod($length, 256));
		$value	.=	chr(0x0);

		return $value;
	}

	// Create Preconnect packet
	public function preCheck()
	{
		$Packet = ""; // Return packet

		// 0x00000000 Client Request
		// Gi&#7889;ng str_pad(chr(0x0), 4)
		$Packet.= chr(0x0).chr(0x0).chr(0x0).chr(0x0);

		// SessionID
		$Packet.= chr(0x0).chr(0x0).chr(0x0).chr(0x0);

		return $this->getHeader(0).chr(0x4C).$Packet;
	}

	// Create Challenge Request packet
	public function getChallenge($userName)
	{
		//Client Request & SessionID
		$status = chr(0x0).chr(0x0).chr(0x0).chr(0x0);
		$status.= chr(0x0).chr(0x0).chr(0x0).chr(0x0);

		$Packet = ""; // Return packet
		$Packet .= "1".$this->sepStr.$userName.$this->sepStr;

		return $this->getHeader(strlen($Packet)).chr(0x57).$status.$Packet;
	}

	// Create Login packet
	public function Login($Username, $Challenge, $Y, $T, $Crumb, $Invisible)
	{
		$Packet = ""; // Return packet
		/*
		 * Client receives the challenge string, the client will
		 * then add the value of the challenge string
		 * to the password and compute a MD5 hash.
		 */
		$hash		= $this->MD5($Crumb.$Challenge);
		$encodePass = trim($this->Y64($hash));

		// Sign in invisible
		$invisibleByte = "";
		if ($Invisible)
		{
			$invisibleByte = chr(0x0).chr(0x0).chr(0x0).chr(0xC).chr(0x0).chr(0x5A).chr(0x14).chr(0xFB);
		}
		else
		{
			$invisibleByte = chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x57).chr(0x8C).chr(0xAA);
		}

		$Packet	.= "1".$this->sepStr.$Username.$this->sepStr."0".$this->sepStr.$Username.$this->sepStr;
		$Packet	.= "277".$this->sepStr.$Y.$this->sepStr."278".$this->sepStr.$T.$this->sepStr;
		$Packet	.= "307".$this->sepStr.$encodePass.$this->sepStr."244".$this->sepStr."0".$this->sepStr;
		$Packet	.= "2".$this->sepStr.$Username.$this->sepStr."2".$this->sepStr."1".$this->sepStr;
		$Packet	.= "59".$this->sepStr."B.86n2ttt5so3nn&b=3&s=bf".$this->sepStr;
		$Packet	.= "98".$this->sepStr."us".$this->sepStr."135".$this->sepStr.$this->clientVer.$this->sepStr;

		return $this->getHeader(strlen($Packet))."T".$invisibleByte.$Packet;
	}

	// Create Friend Avatar Icon packet
	public function normalCheck($username, $target)
	{
		//Client Request & SessionID
		$status = chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0);
		$Packet = ""; // Return packet

		$Packet .= "1".$this->sepStr.$username.$this->sepStr;
		$Packet .= "5".$this->sepStr.$target.$this->sepStr;
		$Packet .= "57".$this->sepStr.$this->sepStr;
		$Packet .= "13".$this->sepStr."1".$this->sepStr;

		return $this->getHeader(strlen($Packet)).chr(0xBE).$status.$Packet;
	}

	// Create P2P Request packet
	public function P2PScan($username, $target)
	{
		//Client Request & SessionID
		$status = chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0);
		$Packet = ""; // Return packet

		$Packet .= "1".$this->sepStr.$username.$this->sepStr;
		$Packet .= "4".$this->sepStr.$username.$this->sepStr;
		$Packet .= "12".$this->sepStr."LTQ1Njg4ODUxMA==".$this->sepStr;
		$Packet .= "61".$this->sepStr."0".$this->sepStr."2".$this->sepStr.$this->sepStr;
		$Packet .= "5".$this->sepStr.$target.$this->sepStr;
		$Packet .= "13".$this->sepStr."0".$this->sepStr;
		$Packet .= "49".$this->sepStr."PEERTOPEER".$this->sepStr;

		return $this->getHeader(strlen($Packet)).chr(0x4F).$status.$Packet;
	}

	// Create Send IM packet
	public function sendPM($username, $target, $message)
	{
		//5A 55 AA 56 00 4E 86 62
		//5A 55 AA 56 00 5F 89 DD
		$status = chr(0x5A).chr(0x55).chr(0xAA).chr(0x56).chr(0x0).chr(0x0).chr(0x0).chr(0x0);

		$Packet = ""; //Packet to send

		$Packet	.= "0".$this->sepStr.$username.$this->sepStr;
		$Packet .= "1".$this->sepStr.$username.$this->sepStr;
		$Packet .= "5".$this->sepStr.$target.$this->sepStr;
		$Packet	.= "14".$this->sepStr.$message.$this->sepStr;

		return $this->getHeader(strlen($Packet)).chr(0x06).$status.$Packet;
	}

	// Create Send sms packet
	public function sendSms($phone, $message)
	{
		$status = chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0);

		$Packet = ""; //Packet to send

		$Packet	.= "4".$this->sepStr.$phone.$this->sepStr;
		$Packet	.= "64".$this->sepStr.$message.$this->sepStr;

		return $this->getHeader(strlen($Packet)).chr(0x2ea).$status.$Packet;
	}

	// Create Send IM packet
	public function sendTyping($username, $target,$type=1)
	{
		//5A 55 AA 56 00 4E 86 62
		//5A 55 AA 56 00 5F 89 DD
		$status = chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0);

		$Packet = ""; //Packet to send

		$Packet	.= "4".$this->sepStr.$username.$this->sepStr;
		$Packet .= "5".$this->sepStr.$target.$this->sepStr;
		$Packet .= "13".$this->sepStr.$type.$this->sepStr;
		$Packet	.= "14".$this->sepStr.' '.$this->sepStr;
		$Packet	.= "49".$this->sepStr.'TYPING'.$this->sepStr;

		return $this->getHeader(strlen($Packet)).chr(0x4B).$status.$Packet;
	}


	// Create Deny Buddy packet
	public function denyBuddy($username, $target, $message)
	{
		//Client Request & SessionID
		$status = chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0);
		$Packet = ""; // Return packet

		$Packet .= "1".$this->sepStr.$username.$this->sepStr;
		$Packet .= "7".$this->sepStr.$target.$this->sepStr;
		$Packet	.= "14".$this->sepStr.$message.$this->sepStr;

		return $this->getHeader(strlen($Packet)).chr(0x86).$status.$Packet;
	}

	// Create Add Buddy packet
	public function addBuddy($username, $target, $group)
	{
		//Client Request & SessionID
		$status = chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0);
		$Packet = ""; // Return packet

		//$Packet .= "14".$this->sepStr."Introduction".$this->sepStr;
		$Packet	.= "65".$this->sepStr.$group.$this->sepStr;
		$Packet	.= "97".$this->sepStr."1".$this->sepStr;
		//$Packet	.= "216".$this->sepStr."First Name".$this->sepStr;
		//$Packet	.= "254".$this->sepStr."Last Name".$this->sepStr;
		$Packet .= "1".$this->sepStr.$username.$this->sepStr;
		$Packet .= "302".$this->sepStr."319".$this->sepStr;
		$Packet .= "300".$this->sepStr."319".$this->sepStr;
		$Packet .= "7".$this->sepStr.$target.$this->sepStr;
		$Packet .= "301".$this->sepStr."319".$this->sepStr;
		$Packet .= "303".$this->sepStr."319".$this->sepStr;

		return $this->getHeader(strlen($Packet)).chr(0x83).$status.$Packet;
	}

	// Create Magic Login packet
	public function newLogin($Y, $T)
	{
		$packet = "6".$this->sepStr.$Y." ".$T.$this->sepStr;
		$header = "YMSG".chr(0x0).chr(0x10);
		$header .= chr(0x0).chr(0x0);
		$header .= chr(intval(strlen($packet) / 256)).chr(fmod(strlen($packet), 256));
		$header .= chr(0x02).chr(0x26).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0).chr(0x0);

		return $header.$packet;
	}

	// Get Login cookie info
	public function getLoginInfo($userName, $passWord, $chl)
	{
		$token	= "";
		$url	= "https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=&login=".$userName."&passwd=".$passWord;//."&chal=".$chl;
		$buff	= $this->curl($url);
		$status = $buff[0];
		/*
			0	 - OK
			100  - if username or password is missing.
			1013 - username contains @yahoo.com or similar which needs removing.
			1212 - is the username or password is incorrect.
			1213 - is a security lock from to many failed login attempts
			1214 - is a security lock
			1218 - if the account has been deactivated by Yahoo
			1235 - if the username does not exist.
			1236 - locked due to to many login attempts
		*/
		#echo "Status Token: ".$status;
		if ($status == '0')
		{
			$token = $this->parseToken($buff, "ymsgr=", chr(0xA));
		}
		else
		{
			// Your token here
			$token = "ALAyy0u_ChIwdguQv.vNBTorupqAtcnJhsQjXgDKLcxGPW8-"; //xp20688
		}

		$url 		= trim("https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts=&token=".$token);
		$content	= $this->curl($url);

		return $content;
	}

	// Parse data from string
	public function parseToken($src, $begin, $end)
	{
		$start 	= substr($src, strpos($src, $begin) + strlen($begin));
		$result	= substr($start, 0, strpos($start, $end));
		return $result;
	}

	// Raw MD5 function
	public function MD5($str)
	{
		$md5 = md5($str);
		$ctx = '';
		for ($i = 0; $i < 32; $i += 2)
		{
			$ctx .= chr(hexdec($md5{$i} . $md5{$i + 1}));
		}
		return $ctx;
	}

	// Yahoo Base64 function
	public function Y64($str)
	{
		$length = 16;
		$string = base64_encode(substr($str, 0, $length));
		$string = str_replace('+', '.', $string);
		$string = str_replace('/', '_', $string);
		$string = str_replace('=', '-', $string);
		return $string;
	}

	// Get Packet Type function
	function getPacketType($data)
	{
		return substr($data, 11, 1);
	}

	// Format time function
	public function timeFormat($start, $end)
	{
		$time = substr(($end - $start), 0, 7);
		return str_replace('-', '', $time);
	}

	// ReadLine from text file function
	public function readln($file, $line_number)
	{
		/*** read the file into the iterator ***/
		$file_obj = new SplFileObject($file);
		/*** seek to the line number ***/
		$file_obj->seek($line_number);
		/*** return the current line ***/
		return $file_obj->current();
	}

	function set_Cache($name,$value)
	{
		$value=$value."";
		$file="cachephp.txt";
		$f=@fopen($file,"r");
		if($f)
		{
			$data="";
			while(!feof($f))
			{
				$data.=fgets($f);
			}
			$match="((".$name."\|\*\|)([^\*]*)(\(\*\_\*\)))";
			if(preg_match($match,$data))
			{
				$data=preg_replace($match,$name."|*|".$value."(*_*)",$data);
			}else
			{
				$data.=$name."|*|".$value."(*_*)";
			}
			@fclose($f);
			$f=@fopen($file,"w");
			@fwrite($f,$data);
			@fclose($f);
		}
		@fclose($f);
	}
	function get_Cache($name)
	{
		$file="cachephp.txt";
		$f=@fopen($file,"r");
		if($f)
		{
			$data="";
			while(!feof($f))
			{
				$data.=fgets($f);
			}
			$arr=explode("(*_*)",$data);
			foreach($arr as $i=>$vl)
			{
				$arr2=explode("|*|",$vl);
				if($name==$arr2[0])
				{
					return $arr2[1];
				}
			}
		}
		@fclose($f);
		return null;
	}
    
    function connect($uid,$pwd)
    {
        $this->uid      = $uid;
        $this->server   = $this->getServerIP();
        $this->fp = fsockopen($this->server, 80, $errno, $errstr, 2) or die('Cannot connect!!!');
    	if ($this->fp != null)
    	{
    		stream_set_timeout($this->fp, 1);
    	}
        
        // Create preConnect Packet
    	$preChk = $this->preCheck();
    
    	fwrite($this->fp, $preChk);
    
    	// Preconnect result
    	$chkStr 	= fread($this->fp, 20);
    
    	if ($this->getPacketType($chkStr) == 'L')
    	{
    		print 'Precheck success!!!<br/>';
    
    		// Create Challenge Request Packet
    		$chkStr = $this->getChallenge($uid);
    		fwrite($this->fp, $chkStr);
    		$chlStr = fread($this->fp, 150);
    	}
    
    	if ($this->getPacketType($chkStr) == 'W')
    	{
    		print 'Challenge Succes!!!<br/>';
    
    		// Get Status and Challenge String from Server Response
    		$status 	= $this->parseToken($chlStr, "13".$this->sepStr, chr(0xC0));
    		$challenge 	= $this->parseToken($chlStr, "94".$this->sepStr, chr(0xC0));
    
    
    		// Get and Parse Login Cookie Info
    		$info		= $this->getLoginInfo($uid, $pwd, $challengnge);
    		$crumb		= $this->parseToken($info, "crumb=", chr(0xD));
    		$T			= $this->parseToken($info, "T=", chr(0xD));
    		$Y			= $this->parseToken($info, "Y=", chr(0xD));
    
    
    		// Create Login Packet
    		$loginStr	= $this->Login($uid, $challenge, $Y, $T, $crumb,false);
    		fwrite($this->fp, $loginStr);
    
    		// Login Result
    		$loginRes	= fread($this->fp, 1024);
    	}
    
    	if ($this->getPacketType($loginRes) == 'U')
    	{
    		print 'Login Succes!!!<br/>';
    
    		// Account info: Friend List, Ignore List, etc...
    		$nextData = '';
    		do {
    			$nextData = fread($this->fp, 500);
    		} while (strlen($nextData) > 0);
    	}
    }
    
    function sendMsg($target,$msg)
    {
        $sendPM		= $this->sendPM($this->uid, $target, $msg);
	   fwrite($this->fp, $sendPM);
	   $result = fread($this->fp, 1024);
    }
}
?>
